#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <ctype.h>
#include <string.h>
#include <stdio.h>

#include "demo_system.h"
#include "dev_access.h"
#include "gpio.h"
#include "uart.h"
#include "pwm.h"
#include "timer.h"
#include "ise.h"

// #include "./Dilithium/params.h"
// #include "./Dilithium/packing.h"
// #include "./Dilithium/polyvec.h"
#include "poly.h"
// #include "./Dilithium/symmetric.h"
// #include "./Dilithium/fips202.h"
// #include "./Dilithium/randombytes.h"
// #include "./Dilithium/sign.h"
// #include "./Dilithium/masked_sign.h"
// #include "./Dilithium/masking_interface.h"
// #include "./Dilithium/masked_polyvec_operations.h"
// #include "./Dilithium/packing.h"
// #include "./Masking/random.h"

// format conversion

int  xtoi( char x ) {
  if     ( ( x >= '0' ) && ( x <= '9' ) ) {
    return ( ( int )( x ) - ( int )( '0' )      );
  }
  else if( ( x >= 'a' ) && ( x <= 'f' ) ) {
    return ( ( int )( x ) - ( int )( 'a' ) + 10 );
  }
  else if( ( x >= 'A' ) && ( x <= 'F' ) ) {
    return ( ( int )( x ) - ( int )( 'A' ) + 10 );
  }

  return -1;
}

char itox( int  x ) {
  if     ( ( x >=  0 ) && ( x <=  9 ) ) {
    return ( char )( ( int )( '0' ) + x       );
  }
  else if( ( x >= 10 ) && ( x <= 15 ) ) {
    return ( char )( ( int )( 'A' ) + x - 10  );
  }

  return '?';
}

void xtob( uint8_t* r, char* x, int n ) {
  for( int i = 0; i < n; i++ ) {
    r[ i ]  = ( uint8_t )( ( xtoi( *x++ ) & 0xF ) << 4 );
    r[ i ] |= ( uint8_t )( ( xtoi( *x++ ) & 0xF ) << 0 );
  }
}

void btox( char* r, uint8_t* x, int n ) {
  for( int i = 0; i < n; i++ ) {
    *r++ = itox( ( uint8_t )( ( x[ i ] >> 4 ) & 0xF ) );
    *r++ = itox( ( uint8_t )( ( x[ i ] >> 0 ) & 0xF ) );
  }

  *r++ = '\x00';
}


// I/O via UART: raw

void    uart_wr  ( uart_t uart, uint8_t x ) {
  while( DEV_READ( uart + UART_STATUS_REG ) & UART_STATUS_TX_FULL  ) {
    /* spin */
  }

         DEV_WRITE( uart + UART_TX_REG, x );
}

uint8_t uart_rd  ( uart_t uart            ) {
  while( DEV_READ( uart + UART_STATUS_REG ) & UART_STATUS_RX_EMPTY ) { 
    /* spin */
  }

  return DEV_READ ( uart + UART_RX_REG    );
}


// I/O via UART: structured

void octetstr_wr( uart_t uart, uint8_t* x, int n_x ) {
             uart_wr( uart, itox( ( n_x      >> 4 ) & 0xF ) );
             uart_wr( uart, itox( ( n_x      >> 0 ) & 0xF ) );

             uart_wr( uart, ':'    );

  for( int i = 0; i < n_x; i++ ) {
             uart_wr( uart, itox( (   x[ i ] >> 4 ) & 0xF ) );
             uart_wr( uart, itox( (   x[ i ] >> 0 ) & 0xF ) );
  }

             uart_wr( uart, '\x0D' );
}

int  octetstr_rd( uart_t uart,       uint8_t* r, int n_r ) {
  int n  = ( ( xtoi( uart_rd( uart ) ) & 0xF ) << 4 );
  
      n |= ( ( xtoi( uart_rd( uart ) ) & 0xF ) << 0 );

  if( n > n_r ) {
    return 0xF1000000 |              ( n << 0 );
  }

  uint8_t t;

  if( ( t = uart_rd( uart ) ) != ':' ) {
    return 0xF2000000 | ( t << 8 ) | ( n << 0 );
  }

  int i = 0;

  for( i = 0; i < n; i++ ) {
    r[ i ] = 0;

    if( ( ( t = uart_rd( uart ) ) == '\x0D' ) || !isxdigit( t ) ) {
      break;
    }
    r[ i ] |= ( uint8_t )( xtoi( t ) & 0xF ) << 4;

    if( ( ( t = uart_rd( uart ) ) == '\x0D' ) || !isxdigit( t ) ) {
      break;
    }
    r[ i ] |= ( uint8_t )( xtoi( t ) & 0xF ) << 0;
  }

  while( t != '\x0D' ) {
    t = uart_rd( uart );
  }

  return i;
}

void ack_wr( uart_t uart, bool x ) {
  if( x ) {
    uart_wr( uart, '+' ); uart_wr( uart, '\x0D' );
  }
  else {
    uart_wr( uart, '-' ); uart_wr( uart, '\x0D' );
  }
}

bool ack_rd( uart_t uart         ) {
  uint8_t x = uart_rd( uart );
  uint8_t t = uart_rd( uart );

  while( t != '\x0D' ) {
    t = uart_rd( uart );
  }

  return ( x == '+' ) ? ( true ) : ( false );
}

/////////////////////////////////////////////////////////////////////////////



int main( int argc, char argv[] ) {

  set_global_interrupt_enable( 0 );

  // uint8_t rhoprime[48] = {43, 22, 20, 232, 233, 125, 226, 97, 3, 147, 32, 220, 45, 105, 190, 21, 220, 37, 114, 161, 21, 165, 34, 187, 98, 108, 34, 116, 162, 168, 181, 66, 143, 23, 226, 186, 213, 125, 186, 12, 224, 230, 125, 0, 247, 20, 75, 107};
  // uint16_t nonce = 0;
  uint8_t rhoprime[48];
  uint16_t nonce;
  poly y;
  while(1){
    uint8_t temp[2];
    uart_t uart = DEFAULT_UART;

    // input x[0]
    octetstr_rd( uart, rhoprime, 48 );


    // input x[1]
    octetstr_rd( uart, temp, 2 );
    nonce = (uint16_t) temp[0]<<8;
    nonce = (uint16_t) temp[1] ;

    poly_uniform_gamma1(&y, rhoprime, nonce);

    for(int i = 0; i < 4; i++){
      puthex_32(y.coeffs[i]);
      puts(" ");
    }
    puts("\n");
 
  }
  
  set_global_interrupt_enable( 1 );
  return 0;
}
